

// 日志落地模块
// 1，标准输出
// 2，指定文件
// 3，滚动文件(按照文件大小进行滚动)

#ifndef __M_LINK_H__
#define __M_LINK_H__

#include "util.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cassert>
#include <memory>
#include <ctime>

namespace xglog
{
    // 抽象模板基类
    class LogSink
    {
    public:
        LogSink() {}
        virtual ~LogSink(){};
        virtual void log(const char *data, size_t len) = 0;
    };
    using SinkPtr=std::shared_ptr<LogSink>;
    // 1,标准输出
    class StdoutSink : public LogSink
    {
    public:
        ~StdoutSink()
        { 
        }
        void log(const char *data, size_t len)
        {
            // 将前len个字符写入输出流中
            std::cout.write(data, len);
        }
    };

    // 2,指定文件写入
    class FileSink : public LogSink
    {
    public:
        // 在构造函数中，传入指定的文件名，同时完成文件的打开工作
        FileSink(const std::string pathname)
            : _pathname(pathname)
        {
            // 1,首先为该文件创建目录
            util::File::createDrector(util::File::path(_pathname));
            // 2,打开文件,以二进制流的形式，并且写入是按照追加方式写入的
            _ofs.open(_pathname, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        virtual ~FileSink()
        {
            _ofs.close();
        }
        // 向指定文件写入
        void log(const char *data, size_t len)
        {
            _ofs.write(data, len);
            // 断言写入是否出错
            assert(_ofs.good());
        }

    private:
        std::string _pathname;
        // ofatream是ostream的一个子类，都是用来处理输入输出工作
        // ostream提供了文件输出的功能，主要用于为输出设备发送数据
        // ofstream可以将数据写入到指定文件中，专为文件输出设计
        std::ofstream _ofs;
    };

    // 3,滚动文件写入
    // 当文件超过一定大小，就更换文件
    class RollBySizeSink : public LogSink
    {
    public:
        RollBySizeSink(const std::string basename, size_t maxsize)
            : _basename(basename), _max_size(maxsize), _cur_size(0), _count(0)
        {
            std::string pathname = createNewFile();
            // 1,首先为该文件创建目录
            util::File::createDrector(util::File::path(pathname));
            // 2,打开文件,以二进制流的形式，并且写入是按照追加方式写入的
            _ofs.open(pathname, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        // 如果当前文件的大小已经超过最大限制
        // 重新建一个文件输出日志
        void log(const char *data, size_t len)
        {
            if (_cur_size >= _max_size)
            {
                _ofs.close();
                std::string pathname = createNewFile();
                // 1,首先为该文件创建目录
                util::File::createDrector(util::File::path(pathname));
                // 2,打开文件,以二进制流的形式，并且写入是按照追加方式写入的
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
                _cur_size=0;
            }
            _ofs.write(data, len);
            assert(_ofs.good());
            _cur_size+=len;
        }
        ~RollBySizeSink()
        {
            _ofs.close();
        }
    private:
        // 创建一个新的文件
        std::string createNewFile()
        {
            // 获取系统时间，以时间来作为系统名
            time_t t=util::Date::datetime();
            struct tm lt;
            localtime_r(&t, &lt);

            std::stringstream filename;
            filename << _basename;
            filename << lt.tm_year+1900; // 年
            filename << lt.tm_mon+1;  // 月
            filename << lt.tm_mday; // 日
            filename << lt.tm_hour; // 时
            filename << lt.tm_min;  // 分
            filename << lt.tm_sec;  // 秒
            filename << "-";
            filename << _count++;
            filename << ".log";

            return filename.str();
        }

    private:
        std::string _basename; // 基本文件名,如base-1,base-2,base-3
        std::ofstream _ofs;
        size_t _max_size; // 文件最多可以写入的数据大小
        size_t _cur_size; // 当前文件已经写入的大小
        size_t _count;     // 生成文件时文件名使用
    };

    //通过简单工厂模式初始化对应的日志落地方向
    class SinkFactory
    {
        public:
        template<typename SinkType,typename ...Args>
        static SinkPtr createSink(Args&& ...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}

#endif